home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 September
/
Macworld (1997-09).dmg
/
Shareware World
/
Utilities
/
Text Processing
/
Alpha
/
Tcl
/
Modes
/
htmlEngine.tcl
< prev
next >
Wrap
Text File
|
1997-03-04
|
49KB
|
1,597 lines
#===============================================================================
#
# htmlEngine.tcl (called from html.tcl)
#
# Part of HTML mode 1.4.1
#
# General Support Routines
#
# Copyright 1996, 1997 by Johan Linde <jl@theophys.kth.se>.
# This software may be used freely, and distributed freely, as long as
# the receiver is not obligated in any way by receiving it.
#
# If you make improvements to this file, please share them!
#
#===============================================================================
proc htmlIsUnsignedInteger {str1} {
return [regexp {^[0-9]+$} [string trim $str1]]
}
proc htmlIsPositiveInteger {str1} {
return [expr ([htmlIsUnsignedInteger $str1] && ![regexp {^0+$} [string trim $str1]])]
}
proc htmlIsInteger {str} {
return [regexp {^-?[0-9]+$} [string trim $str]]
}
# Checks to see if the current window is empty, except for whitespace.
proc htmlIsEmptyFile {} {
return [htmlIsWhite [getText 0 [maxPos]]]
}
# Quoting of strings for meta tags.
proc htmlQuote {str} {
regsub -all "#" $str {#;} str
regsub -all "\"" $str {#qt;} str
regsub -all "<" $str {#lt;} str
regsub -all ">" $str {#gt;} str
return $str
}
proc htmlUnQuote {str} {
regsub -all {#qt;} $str "\"" str
regsub -all {#lt;} $str "<" str
regsub -all {#gt;} $str ">" str
regsub -all {#;} $str "#" str
return $str
}
proc htmlCommentStrings {} {
if {![catch {search -f 0 -r 1 -i 1 -m 0 {<SCRIPT([ \t\r]+[^>]*>|>)} [getPos]} res1] &&
([catch {search -f 0 -r 1 -i 1 -m 0 {</SCRIPT>} [getPos]} res2] ||
[lindex $res1 0] > [lindex $res2 0])} {
return [list "/* " " */"]
} else {
return [list "<!-- " " -->"]
}
}
# Create a string for URL mapping in Big Brother.
proc htmlURLmap {} {
global HTMLmodeVars
set urlmap {}
foreach hp $HTMLmodeVars(homePages) {
set fld "[htmlURLescape [lindex $hp 0] 1]/"
regsub -all ":" $fld "/" fld
set url [htmlURLescape "[lindex $hp 1][lindex $hp 2]"]
lappend urlmap "Msta:“$url”, Mend:“file:///$fld”"
append urlmap ","
}
set urlmap [string trimright $urlmap ","]
return $urlmap
}
# Escapes certain characters in URLs.
proc htmlURLescape {str {slash 0}} {
set hexa {0 1 2 3 4 5 6 7 8 9 A B C D E F}
set nstr ""
set exp "\[\001- \177-ˇ%<>\"#\?=&;|\\{\\}\\`^"
if {$slash} {append exp "/"}
append exp "\]"
while {[regexp -indices $exp $str c]} {
set asc [htmlAscii [string index $str [lindex $c 0]]]
append nstr [string range $str 0 [expr [lindex $c 0] - 1]]
append nstr % [lindex $hexa [expr $asc / 16]] [lindex $hexa [expr $asc % 16]]
set str [string range $str [expr [lindex $c 1] + 1] end]
}
return "$nstr$str"
}
proc htmlURLescape2 {str} {
set url ""
regexp {[^#]*} $str url
set anchor [string range $str [string length $url] end]
return "[htmlURLescape $url]$anchor"
}
# Translate escaped characters in URLs.
proc htmlURLunEscape {str} {
set hexa {0 1 2 3 4 5 6 7 8 9 A B C D E F}
set nstr ""
while {[regexp -indices {%[0-9A-F][0-9A-F]} $str hex]} {
append nstr [string range $str 0 [expr [lindex $hex 0] - 1]]
append nstr [htmlAscii [expr 16 * [lsearch $hexa [string index $str [expr [lindex $hex 0] + 1]]] \
+ [lsearch $hexa [string index $str [expr [lindex $hex 0] + 2]]]] 1]
set str [string range $str [expr [lindex $hex 1] + 1] end]
}
return "$nstr$str"
}
# Makes a line for browser error window.
proc htmlBrwsErr {fil l lnum ln text path} {
return "$fil[format "%$l\s" ""]; Line $lnum:[format "%$ln\s" ""]$text\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$path\r"
}
proc htmlIsTextFile {fil cmd} {
getFileInfo $fil filetest
if {$filetest(type) != "TEXT"} {
$cmd "[file tail $fil] is not a text file."
return 0
}
return 1
}
proc htmlAllSaved {msg} {
set dirty 0
foreach w [winNames] {
getWinInfo -w $w arr
if {$arr(dirty)} {set dirty 1; break}
}
if {$dirty} {
set yn [eval [concat askyesno $msg]]
if {$yn == "yes"} {saveAll}
return $yn
}
return yes
}
proc htmlIsThereAHomePage {} {
global HTMLmodeVars
if {![llength $HTMLmodeVars(homePages)]} {
alertnote "You must set a home page folder."
htmlHomePages
}
return [llength $HTMLmodeVars(homePages)]
}
proc htmlWhichHomePage {msg} {
global HTMLmodeVars
foreach hp $HTMLmodeVars(homePages) {
lappend hplist "[lindex $hp 1][lindex $hp 2]"
}
if {[catch {listpick -p "Select home page to $msg." $hplist} hp] || ![string length $hp]} {error ""}
set home [lindex $HTMLmodeVars(homePages) [lsearch -exact $hplist $hp]]
if {![file exists [lindex $home 0]] || ![file isdirectory [lindex $home 0]]} {
alertnote "Can't find the folder for [lindex $home 1][lindex $home 2]"
error ""
}
return $home
}
# Checks if a folder contains a home page folder or an include folder as a subfolder.
proc htmlContainHpFolder {folder} {
global HTMLmodeVars
foreach p $HTMLmodeVars(homePages) {
foreach i {0 4} {
if {[llength $p] == $i} {continue}
if {[string match "$folder:*" "[lindex $p $i]:"] && "[lindex $p $i]:" != "$folder:"} {
return 1
}
}
}
return 0
}
# Asks for a folder and checks that it is not an alias.
proc htmlGetDir {prompt} {
while {1} {
if {[file isdirectory [set folder [get_directory -p $prompt]]]} {
break
} else {
alertnote "Sorry! Cannot resolve aliases."
}
}
return [string trimright $folder :]
}
proc htmlAscii {char {num 0}} {
if {$char == ""} {return 0}
set str "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
append str "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
append str " !\"#\$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
append str "\[\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177ÄÅÇÉÑÖÜáàâäãåçéèêë"
append str "íìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈Δ«»… ÀÃÕŒœ–—"
append str "“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ"
if {$num} {
return [string index $str [expr $char - 1]]
} else {
return [expr 1 + [string first $char $str]]
}
}
proc htmlNotYet {} {
alertnote "Not yet, but coming soon."
}
proc htmlDisabled {} {
alertnote "Disabled function!"
error "Disabled function!"
}
proc htmlSetCase {elem} {
global HTMLmodeVars
if {$HTMLmodeVars(useLowerCase)} {
return [string tolower $elem]
} else {
return [string toupper $elem]
}
}
# Returns a list of all attributes used in any HTML element.
proc htmlGetAllAttrs {} {
global htmlElemAttrOptional1 htmlElemAttrRequired1 htmlElemEventHandler1
foreach elem [array names htmlElemAttrOptional1] {
if {[info exists htmlElemAttrRequired1($elem)]} {
append allHTMLattrs " " $htmlElemAttrRequired1($elem)
}
append allHTMLattrs " " $htmlElemAttrOptional1($elem)
if {[info exists htmlElemEventHandler1($elem)]} {
append allHTMLattrs " " [string toupper $htmlElemEventHandler1($elem)]
}
}
return $allHTMLattrs
}
# Snatch the current selection into htmlCurSel, set flag whether there is one
proc htmlGetSel {} {
global htmlCurSel htmlIsSel
set htmlCurSel [string trim [getSelect]]
set htmlIsSel [string length $htmlCurSel]
}
#===============================================================================
# File routines
#===============================================================================
# Determines width and height of a GIF file.
proc htmlGIFWidthHeight {fil} {
if {[catch {open $fil r} fid]} {return}
seek $fid 6 start
set width [expr [htmlReadOne $fid] + 256 * [htmlAscii [read $fid 1]]]
set height [expr [htmlReadOne $fid] + 256 * [htmlAscii [read $fid 1]]]
close $fid
return [list $width $height]
}
# Extracts width and height of a jpeg file.
# Algorithm from the perl script 'wwwimagesize' by
# Alex Knowles, alex@ed.ac.uk
# Andrew Tong, werdna@ugcs.caltech.edu
proc htmlJPEGWidthHeight {fil} {
if {[catch {open $fil r} fid]} {return}
if {[htmlAscii [read $fid 1]] != 255 || [htmlAscii [read $fid 1]] != 216} {return}
set ch ""
while {![eof $fid]} {
while {[htmlAscii $ch] != 255 && ![eof $fid]} {set ch [read $fid 1]}
while {[htmlAscii $ch] == 255 && ![eof $fid]} {set ch [read $fid 1]}
if {[set asc [htmlAscii $ch]] >= 192 && $asc <= 195} {
seek $fid 3 current
set height [expr 256 * [htmlAscii [read $fid 1]] + [htmlReadOne $fid]]
set width [expr 256 * [htmlAscii [read $fid 1]] + [htmlReadOne $fid]]
close $fid
return [list $width $height]
} else {
set ln [expr 256 * [htmlAscii [read $fid 1]] + [htmlAscii [read $fid 1]] - 2]
if {$ln < 0} {break}
seek $fid $ln current
}
}
close $fid
}
# Reads one character from an image file.
# For some mysterious reason 10 and 13 has to be swapped.
proc htmlReadOne {fid} {
set c [htmlAscii [read $fid 1]]
if {$c == 13} {
set c 10
} elseif {$c == 10} {
set c 13
}
return $c
}
# Returns the URL to the current window.
# Called with 0 if called from htmlGetFile.
# Called with 1 if called from HTMLDblClick. (0 or 1 determines the error message.)
proc htmlThisFilePath {errorMsg} {
global HTMLmodeVars
set thisFile [stripNameCount [lindex [winNames -f] 0]]
# Look for BASE element.
if {![catch {search -s -f 1 -r 1 -i 1 -m 0 {<BASE[ \t\r]+[^>]*>} 0} res] && \
[regexp -nocase {HREF=\"?([^ \t\r\">]+)} [getText [lindex $res 0] \
[lindex $res 1]] dum href]} {
if {[catch {htmlBASEpieces $href} basestr]} {
alertnote "Window contains invalid BASE element. Ignored."
} else {
return $basestr
}
}
# Check if window is saved.
if {![file exists $thisFile]} {
if {$errorMsg} {
set etxt "You must save the window, otherwise it cannot be determined\
where the link is pointing."
} else {
set etxt "You must save the window. If you save, you will then be prompted\
for a file to link to."
}
if {[lindex [dialog -w 400 -h 100 -t $etxt 10 10 390 60 \
-b Save 20 70 85 90 \
-b Cancel 110 70 175 90] 1]} {
return
}
if {![catch {saveAs "Untitled.html"}]} {
set thisFile [stripNameCount [lindex [winNames -f] 0]]
} else {
return
}
}
return [htmlBASEfromPath $thisFile]
}
# Returns URL to file.
proc htmlBASEfromPath {path} {
global HTMLmodeVars
foreach p $HTMLmodeVars(homePages) {
if {(![set i 0] && [string match "[lindex $p $i]:*" "$path:"]) ||
([llength $p] == 5 && [set i 4] && [string match "[lindex $p $i]:*" "$path:"])} {
set path [string range $path [expr [string length [lindex $p $i]] + 1] end]
regsub -all {:} $path {/} path
return [list [lindex $p 1] [lindex $p 2] $path [lindex $p 0] $i [lindex $p 4]]
}
}
regsub -all {:} $path {/} path
return [list "file:///" "" $path "" 0]
}
# Splits a BASE URL in pieces.
# NOTE! That this proc returns a shorter list than the proc above, is used in
# HTMLDblClick to determine if the doc contains a BASE tag.
proc htmlBASEpieces {href} {
if {[regexp -indices {://} $href css]} {
if {[set sl [string first / [string range $href [expr [lindex $css 1] + 1] end]]] >=0} {
set base [string range $href 0 [expr [lindex $css 1] + $sl + 1]]
set path [string range $href [expr [lindex $css 1] + $sl + 2] end]
set sl [string last / $path]
set epath [string range $path [expr $sl + 1] end]
set path [string range $path 0 $sl]
} else {
set base [string range $href 0 [lindex $css 1]]
set path ""
set epath [string range $href [expr [lindex $css 1] + 1] end]
}
return [list [htmlURLunEscape $base] [htmlURLunEscape $path] [htmlURLunEscape $epath] ""]
} else {
error "Invalid BASE."
}
}
# Returns toFile including relative path from fromFile.
proc htmlRelativePath {fromFile toFile} {
# Remove trailing /file from fromFile
set fromFile [string range $fromFile 0 [expr [string last / $fromFile] - 1]]
set fromdir [split $fromFile /]
set todir [split $toFile /]
# Remove the common path.
set i 0
while {[llength $fromdir] > $i && [llength $todir] > $i \
&& [lindex $fromdir $i] == [lindex $todir $i]} {
incr i
}
# Insert ../
foreach f [lrange $fromdir $i end] {
append linkTo "../"
}
# Add the path.
append linkTo [join [lrange $todir $i end] /]
return $linkTo
}
# Returns a list of all HTML files in a folder and its subfolders.
proc htmlAllHTMLfiles {folder} {
message "Building file list…"
set folders [list $folder]
while {[llength $folders]} {
set newFolders ""
foreach fl $folders {
append files " " [htmlGetHTMLfiles $fl]
# Get folders in this folder.
if {![catch {glob "$fl:*"} filelist]} {
foreach fil $filelist {
if {[file isdirectory $fil]} {
lappend newFolders $fil
}
}
}
}
set folders $newFolders
}
return $files
}
# Finds all HTML files in a folder
proc htmlGetHTMLfiles {folder} {
global filepats
set files ""
if {![catch {glob -t TEXT $folder:*} filelist]} {
foreach fil $filelist {
foreach suffix $filepats(HTML) {
if {[string match $suffix $fil]} {
lappend files $fil
break
}
}
}
}
return $files
}
# checking = 1: called from htmlCheckLinks
# Scan a list of files for HTML links and check if they point to existing files.
# Some code is taken from grep.tcl
# checking = 0: called from htmlMoveFiles
# Build a list of links which point to the files just moved.
proc htmlScanFiles {files baseURL basePath homepage isInFolder checking filebase {movedFiles ""}} {
global htmlURLAttr winModes HTMLmodeVars
global tileLeft tileTop tileWidth errorHeight
global htmlCaseFolders htmlCaseFiles
set htmlCaseFolders ""; set htmlCaseFiles ""
set chCase $HTMLmodeVars(caseSensitive)
set chAnchor $HTMLmodeVars(checkAnchors)
# Build regular expressions with URL attrs.
set exp "\[ \\t\\n\\r\]+("
foreach attr $htmlURLAttr {
append exp "$attr|"
}
set exp [string trimright $exp |]
append exp ")"
set expBase "<base\[ \\t\\n\\r\]+\[^>\]*>"
set expBase2 "(href=)\"?(\[^ \\t\\n\\r\">\]+)\"?"
# set exprr "$exp\"?(\[^ \\t\\n\\r\">\]+)\"?"
set exprr "${exp}(\"\[^\">\]+\"|\[^ \\t\\n\\r>\]+)"
set lines ""
set toModify ""
foreach f $files {
if {[catch {set fid [open $f]}]} {continue}
set base $baseURL
set path $basePath
set hpPath $homepage
if {$isInFolder == ""} {
set epath $f
} else {
set epath [string range $f [expr [string length $isInFolder] + 1] end]
}
regsub -all {:} $epath {/} epath
set baseText ""
message "Looking at [file tail $f]…"
set filecont [read $fid]
close $fid
if {[regexp {\n} $filecont]} {
set newln "\n"
} else {
set newln "\r"
}
# Look for BASE.
if {[regexp -nocase $expBase $filecont thisLine]} {
if {[regexp -nocase $expBase2 $thisLine href b url]} {
if {![catch {htmlBASEpieces $url} basestr]} {
set base [lindex $basestr 0]
set path [lindex $basestr 1]
set epath [lindex $basestr 2]
set hpPath ""
set baseText "(BASE used) "
} else {
set baseText "(Invalid BASE) "
}
}
}
set linenum 1
# Find all links in every line.
while {[regexp -nocase -indices $exprr $filecont href b url]} {
incr linenum [regsub -all $newln [string range $filecont 0 [lindex $url 0]] {} dummy]
set linkTo [htmlURLunEscape [string trim [string range $filecont [lindex $url 0] [lindex $url 1]] \"]]
set nogood 0
if {[catch {htmlPathToFile $base $path $epath $hpPath $linkTo} linkToPath]} {
if {$linkToPath == ""} {
set nogood 1
}
set linkToPath ""
} else {
# Anchors always point to the file itself, unless there's a BASE.
if {[string index $linkTo 0] == "#" && $baseText == ""} {set linkToPath [list $f $f]}
set casePath [lindex $linkToPath 1]
set linkToPath [lindex $linkToPath 0]
}
# If this is BASE HREF, ignore it.
if {[string length $baseText] && [regexp -nocase -indices $expBase $filecont thisLine] \
&& [regexp -nocase $expBase2 [string range $filecont [lindex $thisLine 0] [lindex $thisLine 1]]]\
&& [lindex $thisLine 0] < [lindex $url 0] && [lindex $thisLine 1] > [lindex $url 1]} {
set linkToPath ""
}
if {$checking} {
set anchorCheck 1
set caseOK 1
set fext [file exists $linkToPath]
if {$chAnchor && $linkToPath != "" && [regexp {#} $linkTo] && $fext} {set anchorCheck [htmlCheckAnchor $linkToPath $linkTo]}
if {$chCase && $linkToPath != "" && $fext} {set caseOK [htmlCheckLinkCase $linkToPath $casePath]}
# Does the file exist? Ignore it if it's outside home page folder.
# Then it point to someone else's home page.
if {!$anchorCheck || $nogood || !$caseOK || ( $linkToPath != "" && !$fext)} {
set bText $baseText
if {!$anchorCheck} {append bText "(anchor missing) "}
if {!$caseOK} {append bText "(case doesn't match) "}
if {$homepage == ""} {
append lines [string range $f $filebase end]
} else {
append lines [string range $f [expr [string length $isInFolder] + 1] end]
}
set l [expr 20 - [string length [file tail $f]]]
set ln [expr 5 - [string length $linenum]]
set href [string trim [string range $filecont [lindex $href 0] [lindex $href 1]]]
append lines "[format "%$l\s" ""]; Line $linenum:[format "%$ln\s" ""]$bText$href"\
"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t∞$f\r"
}
} elseif {[lsearch -exact $movedFiles $linkToPath] >=0 } {
set href [string trim [string range $filecont [lindex $href 0] [lindex $href 1]]]
lappend toModify [list $f $linenum $base $path $epath $linkToPath $href]
}
set filecont [string range $filecont [lindex $url 1] end]
}
}
unset htmlCaseFolders htmlCaseFiles
message ""
if {$checking} {
if {[string length $lines]} {
new -n "* Invalid URLs *" -g $tileLeft $tileTop $tileWidth $errorHeight
set name [lindex [winNames] 0]
changeMode [set winModes($name) Brws]
insertText "Incorrect links: (<uparrow> and <downarrow> to browse, <return> to go to file)\r\r$lines"
select [nextLineStart [nextLineStart 0]] [nextLineStart [nextLineStart [nextLineStart 0]]]
setWinInfo dirty 0
setWinInfo read-only 1
scrollUpLine; scrollUpLine
} else {
alertnote "All links are OK."
}
} else {
return $toModify
}
}
# Determine the path to the file "linkTo", as linked from "base path epath".
proc htmlPathToFile {base path epath hpPath linkTo} {
global HTMLmodeVars
# Is this a mailto or news URL or anchor?
if {[string match "mailto:*" [string tolower $linkTo]] ||
[string match "news:*" [string tolower $linkTo]]} {
error $linkTo
}
# remove /file from epath
set sl [string last / $epath]
set efil [string range $epath [expr $sl + 1] end]
set epath [string range $epath 0 $sl]
# anchor points to efil
if {[string index $linkTo 0] == "#"} {set linkTo $efil}
# Remove anchor from "linkTo".
regexp {[^#]*} $linkTo linkTo
# Remove ./ from path
if {[string range $linkTo 0 1] == "./"} {set linkTo [string range $linkTo 2 end]}
# Relative URL beginning with / is relative to server URL.
if {[string index $linkTo 0] == "/"} {
set linkTo "$base[string range $linkTo 1 end]"
}
# Relative URL?
if {![regexp {://} $linkTo]} {
set fromPath [split [string trimright "${path}$epath" /] /]
set toPath [split $linkTo /]
# Back down for every ../
set i 0
foreach tp $toPath {
if {$tp == ".."} {
incr i
} else {
break
}
}
if {$i > [llength $fromPath] } {
error ""
} else {
set path1 [join [lrange $fromPath 0 [expr [llength $fromPath] - $i - 1]] /]
if {[string length $path1]} {append path1 /}
append path1 [join [lrange $toPath $i end] /]
if {[string match "$path*" $path1] && [string length $hpPath]} {
set pathTo [string range $path1 [string length $path] end]
regsub -all {/} $pathTo {:} pathTo
set casePath $pathTo
set pathTo "$hpPath:$pathTo"
if {![file isdirectory $pathTo]} {return [list $pathTo $casePath]}
} elseif {$base == "file:///"} {
regsub -all {/} $path1 {:} pathTo
return [list $pathTo $pathTo]
}
set linkTo "$base$path1"
}
}
foreach hp [concat $HTMLmodeVars(homePages) {{"" file:/// "" ""}}] {
if {[string match "[lindex $hp 1][lindex $hp 2]*" $linkTo] ||
[string trimright "[lindex $hp 1][lindex $hp 2]" /] == $linkTo} {
set pathTo [string range $linkTo [string length "[lindex $hp 1][lindex $hp 2]"] end]
regsub -all {/} $pathTo {:} pathTo
set casePath $pathTo
set pathTo [string trimleft "[lindex $hp 0]:$pathTo" :]
# If link to folder, add default file.
if {[file isdirectory $pathTo]} {
set pathTo [string trimright $pathTo :]
append pathTo ":[lindex $hp 3]"
set casePath [string trimright $casePath :]
append casePath ":[lindex $hp 3]"
}
return [list $pathTo [string trimleft $casePath :]]
}
}
error $linkTo
}
proc htmlCheckAnchor {anchorFile url} {
regexp {[^#]*#(.*)} $url dum anchor
if {[catch {open $anchorFile r} fid]} {return 1}
set filecont [read $fid]
close $fid
set exp "<(\[Aa\]|\[mM\]\[aA\]\[pP\])\[ \t\r\n\]+\[^>\]*\[nN\]\[aA\]\[mM\]\[eE\]=\"?$anchor\"?(>|\[ \t\r\n\]+\[^>\]*>)"
return [regexp $exp $filecont]
}
# Checks that the case in a link match the case in the path to file.
proc htmlCheckLinkCase {path link} {
global htmlCaseFolders htmlCaseFiles
set path [string trimright $path :]
set link [string trimright $link :]
if {[lsearch -exact $htmlCaseFiles $path] >= 0} {return 1}
set path [split $path :]
set plen [llength $path]
set llen [llength [split $link :]]
set j [expr $plen - $llen ? $plen - $llen - 1 : 0]
for {set i $j} {$i < $plen - 1} {incr i} {
set l [lindex $path [expr $i + 1]]
set psub [join [lrange $path 0 $i] :]
if {[lsearch -exact $htmlCaseFolders $psub] < 0} {
lappend htmlCaseFolders $psub
append htmlCaseFiles " " [glob -nocomplain "$psub:*"]
}
if {[lsearch -exact $htmlCaseFiles "$psub:$l"] < 0} {return 0}
}
return 1
}
#
# Carriage returns and tabs (much borrowed from latex.tcl)
#
# A boolean function which takes any string and tests to see if
# that string contains all whitespace characters. Carriage returns
# are considered whitespace, as are spaces and tabs.
proc htmlIsWhite {anyString} {
return [regexp {^[ \t\r]*$} $anyString]
}
# Insert one or two carriage returns at the insertion point if any
# character preceding the insertion point (on the same line)
# is a non-whitespace character.
proc htmlOpenCR {{extrablankline 0}} {
set end [getPos]
set start [lineStart $end]
set text [getText $start $end]
if {![htmlIsWhite $text]} {
set r "\r"
if {$extrablankline} {append r "\r"}
return $r
} elseif {$start > 0 } {
set prevstart [lineStart [expr $start - 1 ]]
set text [getText $prevstart [expr $start - 1]]
if {![htmlIsWhite $text] && $extrablankline} {
return "\r"
} else {
return
}
} else {
return
}
}
# Insert a carriage return at the insertion point if any
# character following the insertion point (on the same line)
# is a non-whitespace character.
proc htmlCloseCR {} {
set start [getPos]
if {![htmlIsWhite [getText $start [nextLineStart $start]]]} {
return "\r"
} else {
return
}
}
# Set up tab mark mechanism.
proc htmlTabGoto {directionIndicator} {
set searchResult [search -s -n -f $directionIndicator -m 0 -i 1 -r 0 {•} [getPos]]
if {![llength $searchResult] || [lindex $searchResult 0] >= [maxPos]} {
beep
message "Tab mark not found."
return 0
} else {
goto [lindex $searchResult 0]
return 1
}
}
proc htmlTabNext {} {
if {[htmlTabGoto 1]} {deleteChar}
}
proc htmlTabPrev {} {
if {[htmlTabGoto 0]} {deleteChar}
}
# Puts up a window with error text.
proc htmlErrorWindow {errHeader errText {cancelButton 0}} {
set errbox "-t {$errHeader} 100 10 400 25"
set hpos 35
foreach err $errText {
lappend errbox -t $err 10 $hpos 400 [expr $hpos + 15]
incr hpos 20
}
if {$cancelButton} {
lappend errbox -b Cancel 105 [expr $hpos + 20 ] 170 [expr $hpos + 40 ]
}
set val [eval [concat dialog -w 430 -h [expr $hpos + 50 ] \
-b OK 20 [expr $hpos + 20 ] 85 [expr $hpos + 40 ] $errbox]]
return [lindex $val 0]
}
#===============================================================================
# Building tags, including element attributes
#===============================================================================
# A couple of functions to get element variables from the right package.
proc htmlGetSomeAttrs {item type num1 pkg} {
global htmlElem${type}$num1 htmlElem${type}3
if {[catch {set atts [set htmlElem${type}${pkg}($item)]}]} {
if {$type == "AttrMore"} {
set atts 0
} else {
set atts {}
}
}
return $atts
}
proc htmlGetRequired {item} {
global htmlPackageToUse
return [htmlGetSomeAttrs $item AttrRequired 1 $htmlPackageToUse]
}
proc htmlGetOptional {item} {
global htmlPackageToUse
return [htmlGetSomeAttrs $item AttrOptional 1 $htmlPackageToUse]
}
proc htmlGetNumber {item} {
global htmlPackageToUse
return [htmlGetSomeAttrs $item AttrNumber 1 $htmlPackageToUse]
}
proc htmlGetChoices {item} {
global htmlPackageToUse
return [htmlGetSomeAttrs $item AttrChoices 1 $htmlPackageToUse]
}
proc htmlGetUsed {item} {
global htmlPackageToUse
if {$htmlPackageToUse == 1} {
set num ""
} else {
set num 3
}
return [htmlGetSomeAttrs $item AttrUsed "" $num]
}
proc htmlGetAttrMore {item} {
global htmlPackageToUse
if {$htmlPackageToUse == 1} {
set num ""
} else {
set num 3
}
return [htmlGetSomeAttrs $item AttrMore "" $num]
}
proc htmlOpenElem {elem {used ""} {pos -1}} {
global HTMLmodeVars
if {$HTMLmodeVars(useBigWindows)} {
return [htmlOpenElemWindow $elem $used $pos]
} else {
return [htmlOpenElemLoop $elem $used $pos]
}
}
# Opening or only tag of an element - include attributes
# Big window with all attributes.
# Return empty string if user clicks "Cancel".
proc htmlOpenElemWindow {elem used wrPos {values ""}} {
global HTMLmodeVars htmlColorName htmlElemEventHandler1
global htmluserColors basicColors htmlPackageToUse
global htmlURLAttr htmlColorAttr htmlWindowAttr
global htmlSpecURL htmlSpecColor htmlSpecWindow htmlWrapPos
set URLs $HTMLmodeVars(URLs)
set Windows $HTMLmodeVars(windows)
# put users colours first
set htmlColors [lsort [array names htmluserColors]]
append htmlColors " " $basicColors
if {![string length $used]} {set used $elem}
set elem [string toupper $elem]
set used [string toupper $used]
# get variables for the element
set reqatts [htmlGetRequired $used]
set numatts [htmlGetNumber $used]
set optatts [htmlGetOptional $used]
set choiceatts [htmlGetChoices $used]
set allatts [concat $reqatts $optatts]
# optionally include event handlers
if {$HTMLmodeVars(inclEventHandler) && $htmlPackageToUse == 1 && \
[info exists htmlElemEventHandler1($used)]} {
set eventatts $htmlElemEventHandler1($used)
append allatts " " $eventatts
} else {
set eventatts ""
}
# if there are attributes to ask about, do so
set text "<"
append text [htmlSetCase $elem]
if {![llength $allatts]} {return "$text>"}
set maxHeight [expr [lindex [getMainDevice] 3] - 115]
set thisPage "Page 1"
# build window with attributes
set invalidInput 1
while {$invalidInput} {
# wrapping
set htmlWrapPos [expr $wrPos == -1 ? [lindex [posToRowCol [getPos]] 1] : $wrPos]
incr htmlWrapPos [expr [string length $text] + 1]
while {1} {
if {$used == "LI IN UL" || $used == "LI IN OL"} {
set pr LI
} else {
set pr $used
}
set box1 "-t {Attributes for $pr} 120 10 450 25"
set box2 "-t {Attributes for $pr} 120 10 450 25"
set box3 "-t {Attributes for $pr} 120 10 450 25"
set page 1
set attrtypes {}
set fileIndex ""
set colorIndex ""
set wpos 10
if {[string length $reqatts]} {
lappend box$page -p 120 30 270 31 -t {Required attributes} 10 35 200 50
set hpos 60
} else {
set hpos 30
}
set attrIndex 2
for {set i 0} {$i < [llength $allatts]} {incr i} {
set attr [lindex $allatts $i]
if {$i == [llength $reqatts]} {
if {$wpos > 20} { incr hpos 20 }
lappend box$page -p 120 $hpos 270 [expr $hpos + 1] \
-t {Optional attributes} 10 [expr $hpos + 5] 200 [expr $hpos + 20]
set wpos 10
incr hpos 30
}
set a2 [string trimright $attr =]
if {[string index $attr [expr [string length $attr] - 1]] != "="} {
# Flag
if {[expr $hpos + 20] > $maxHeight && $wpos < 20 && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set ctxt [lindex $values $attrIndex]
incr attrIndex
} else {
set ctxt 0
}
lappend box$page -c $attr $ctxt $wpos $hpos [expr $wpos + 100] [expr $hpos + 15]
if {$wpos > 20} {
incr hpos 25
set wpos 10
} else {
set wpos 230
}
lappend attrtypes flag
} elseif {([lsearch -exact $htmlURLAttr $attr] >= 0 && [lsearch -exact $htmlSpecURL "${used}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecURL "${used}=$a2"] >= 0} {
# URL
if {$wpos > 20} { incr hpos 25 ; set wpos 10}
if {[expr $hpos + 45] > $maxHeight && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set etxt [lindex $values $attrIndex]
set mtxt [lindex $values [expr $attrIndex + 1]]
incr attrIndex 3
} else {
set etxt ""
set mtxt {No value}
}
lappend box$page -t $attr 10 $hpos 120 [expr $hpos + 15] \
-e $etxt 120 $hpos 450 [expr $hpos + 15] \
-m [concat [list $mtxt {No value}] $URLs] \
120 [expr $hpos + 25] 450 [expr $hpos + 35] \
-b "File…" 10 [expr $hpos + 20] 70 [expr $hpos + 40]
incr hpos 50
lappend attrtypes url
lappend fileIndex [expr $attrIndex - 1]
} elseif {([lsearch -exact $htmlColorAttr $attr] >= 0 && [lsearch -exact $htmlSpecColor "${used}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecColor "${used}=$a2"] >= 0} {
# Color attribute
if {$wpos > 20} { incr hpos 25 ; set wpos 10}
if {[expr $hpos + 25] > $maxHeight && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set etxt [lindex $values $attrIndex]
set mtxt [lindex $values [expr $attrIndex + 1]]
incr attrIndex 3
} else {
set etxt ""
set mtxt {No value}
}
lappend box$page -t $attr 10 $hpos 120 [expr $hpos + 15] \
-e $etxt 120 $hpos 190 [expr $hpos + 15] \
-m [concat [list $mtxt {No value}] $htmlColors] \
200 $hpos 340 [expr $hpos + 15] \
-b "New Color…" 350 $hpos 450 [expr $hpos + 20]
incr hpos 30
lappend attrtypes color
lappend colorIndex [expr $attrIndex - 1]
} elseif {([lsearch -exact $htmlWindowAttr $attr] >= 0 && [lsearch -exact $htmlSpecWindow "${used}!=$a2"] < 0) || \
[lsearch -exact $htmlSpecWindow "${used}=$a2"] >= 0} {
# Window attribute
if {$wpos > 20} { incr hpos 25 ; set wpos 10}
if {[expr $hpos + 25] > $maxHeight && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set etxt [lindex $values $attrIndex]
set mtxt [lindex $values [expr $attrIndex + 1]]
incr attrIndex 2
} else {
set etxt ""
set mtxt {No value}
}
lappend box$page -t $attr 10 $hpos 120 [expr $hpos + 15] \
-e $etxt 120 $hpos 240 [expr $hpos + 15] \
-m [concat [list $mtxt {No value}] \
[concat {_self _top _parent _blank} $Windows]] \
250 $hpos 440 [expr $hpos + 15]
incr hpos 30
lappend attrtypes window
} elseif {[lsearch $numatts "${attr}*"] >= 0} {
# Number
if {[expr $hpos + 20] > $maxHeight && $wpos < 20 && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set etxt [lindex $values $attrIndex]
incr attrIndex
} else {
set etxt ""
}
lappend box$page -t $attr $wpos $hpos [expr $wpos + 100] [expr $hpos + 15] \
-e $etxt [expr $wpos + 110] $hpos [expr $wpos + 150] [expr $hpos + 15]
if {$wpos > 20} {
incr hpos 25
set wpos 10
} else {
set wpos 230
}
lappend attrtypes number
} elseif {[lsearch $choiceatts "${attr}*"] >= 0} {
# Choices
if {[expr $hpos + 20] > $maxHeight && $wpos < 20 && $page < 3} {
incr page
set hpos 40
}
set matches {}
foreach w $choiceatts {
if {[string match "${attr}*" $w]} {
lappend matches [string range $w [string length $attr] end]
}
}
if {[llength values]} {
set mtxt [lindex $values $attrIndex]
incr attrIndex
} else {
set mtxt {No value}
}
lappend box$page -t $attr $wpos $hpos [expr $wpos + 100] [expr $hpos + 15] \
-m [concat [list $mtxt {No value}] $matches] \
[expr $wpos + 110] $hpos [expr $wpos + 205] [expr $hpos + 15]
if {$wpos > 20} {
incr hpos 25
set wpos 10
} else {
set wpos 230
}
lappend attrtypes choices
} else {
# Any other
if {$wpos > 20} { incr hpos 25 ; set wpos 10}
if {[expr $hpos + 20] > $maxHeight && $page < 3} {
incr page
set hpos 40
}
if {[llength values]} {
set etxt [lindex $values $attrIndex]
incr attrIndex
} else {
set etxt ""
}
lappend box$page -t $attr 10 $hpos 120 [expr $hpos + 15] \
-e $etxt 120 $hpos 450 [expr $hpos + 15]
incr hpos 25
lappend attrtypes any
}
}
if {$wpos > 20} { incr hpos 25 }
if {$page == 1} {
set box $box1
} elseif {$page == 2} {
set hpos $maxHeight
set box " -m \{\{$thisPage\} \{Page 1\} \{Page 2\}\} 10 10 85 30 -n \{Page 1\} $box1 -n \{Page 2\} $box2"
} elseif {$page == 3} {
set hpos $maxHeight
set box " -m \{\{$thisPage\} \{Page 1\} \{Page 2\} \{Page 3\}\} 10 10 85 30 -n \{Page 1\} $box1 -n \{Page 2\} $box2 -n \{Page 3\} $box3"
}
set values [eval [concat dialog -w 460 -h [expr $hpos + 50] \
-b OK 20 [expr $hpos + 20] 85 [expr $hpos + 40] \
-b Cancel 110 [expr $hpos + 20] 175 [expr $hpos + 40] $box]]
# If two pages...
if {$page > 1} {
set thisPage [lindex $values 2]
set values [lreplace $values 2 2]
}
# OK button clicked?
if {[lindex $values 0] } { break }
# Cancel button clicked?
if {[lindex $values 1] } { return}
# File button clicked?
foreach fl $fileIndex {
if {[lindex $values $fl]} {
set newFile [htmlGetFile]
if {[string length $newFile]} {
set URLs $HTMLmodeVars(URLs)
set values [lreplace $values [expr $fl - 1] [expr $fl - 1] [lindex $newFile 0]]
if {$used == "IMG" && $fl == 4 && [llength [set widhei [lindex $newFile 1]]]} {
set nnn [expr $htmlPackageToUse == 1 ? 8 : 5]
set values [lreplace $values $nnn $nnn [lindex $widhei 0]]
set values [lreplace $values [expr $nnn + 1] [expr $nnn + 1] [lindex $widhei 1]]
}
}
}
}
# Color button clicked?
foreach cl $colorIndex {
if {[lindex $values $cl]} {
set newcolor [htmlAddNewColor]
if {[string length $newcolor]} {
set htmlColors [concat [list $newcolor] $htmlColors]
set values [lreplace $values [expr $cl - 1] [expr $cl - 1] "$newcolor"]
}
}
}
}
# put everything together
set attrtext ""
set errtext ""
if {[lindex $values 0]} {
set j 2
for {set i 0} {$i < [llength $attrtypes]} {incr i} {
set attr [lindex $allatts $i]
switch [lindex $attrtypes $i] {
url {
set texturl [string trim [lindex $values $j]]
set menuurl [lindex $values [expr $j + 1]]
if {[string length $texturl]} {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes [htmlURLescape2 $texturl]]"]
htmlAddToCache URLs $texturl
} elseif {$menuurl != "No value"} {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes [htmlURLescape2 $menuurl]]"]
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j 3
}
color {
set colortxt [lindex $values $j]
set colorval [lindex $values [expr $j + 1]]
if {[string length $colortxt]} {
set col [htmlCheckColorNumber $colortxt]
if {$col == 0} {
lappend errtext "$attr: $colortxt is not a valid color number."
} else {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes $col]"]
}
} elseif {$colorval != "No value"} {
# Users own color?
if {[info exists htmluserColors($colorval)]} {
set colornum $htmluserColors($colorval)
}
# Predefined color?
if {[info exists htmlColorName($colorval)]} {
set colornum $htmlColorName($colorval)
}
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes $colornum]"]
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j 3
}
window {
set textwin [string trim [lindex $values $j]]
set menuwin [lindex $values [expr $j + 1]]
if {[string length $textwin]} {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes $textwin]"]
htmlAddToCache windows $textwin
} elseif {$menuwin != "No value"} {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes $menuwin]"]
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j 2
}
number {
set numval [string trim [lindex $values $j]]
if {[string length $numval]} {
if {[htmlCheckAttrNumber $used $attr $numval] == 1} {
append attrtext [htmlWrapTag "[htmlSetCase $attr][htmlAddQuotes $numval]"]
} else {
lappend errtext "$attr: [htmlCheckAttrNumber $used $attr $numval]"
}
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j
}
choices {
set choiceval [lindex $values $j]
if {$choiceval != "No value"} {
set qchoice [htmlAddQuotes $choiceval]
if {($used != "LI IN OL" && $used != "OL") || $attr != "TYPE="} {
set qchoice [htmlSetCase $qchoice]
}
append attrtext [htmlWrapTag "[htmlSetCase $attr]$qchoice"]
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j
}
any {
set anyval [lindex $values $j]
# Trim only if it's only spaces.
if {[string trim $anyval] == ""} {set anyval ""}
if {[string length $anyval]} {
htmlOpenExtraThings $used $attr $anyval
if {[lsearch -exact $eventatts $attr] < 0} {
set attr [htmlSetCase $attr]
}
append attrtext [htmlWrapTag "$attr[htmlAddQuotes $anyval]"]
} elseif {[lsearch -exact $reqatts $attr] >= 0} {
lappend errtext "$attr required."
}
incr j
}
flag {
set flagval [lindex $values $j]
if {$flagval} {
append attrtext [htmlWrapTag [htmlSetCase $attr]]
}
incr j
}
}
}
# If everything is OK, add the attribute text to text.
if {![llength $errtext]} {
append text $attrtext
set invalidInput 0
} else {
# Put up alert with the error text.
htmlErrorWindow "Invalid input for $used" $errtext
}
# Some tests that input is ok.
if {!$invalidInput} {set invalidInput [htmlFontBaseTest $text alertnote]}
if {!$invalidInput && $elem == "A" && [set invalidInput [htmlATest $text alertnote]]} {
set text "<[htmlSetCase A]"
}
if {!$invalidInput && $elem == "FRAMESET" && [set invalidInput [htmlFramesetTest $text alertnote]]} {
set text "<[htmlSetCase FRAMESET]"
}
if {!$invalidInput && $elem == "SPACER" && [set invalidInput [htmlSpacerTest $text alertnote]]} {
set text "<[htmlSetCase SPACER]"
}
if {!$invalidInput && $elem == "AREA" && [set invalidInput [htmlAreaTest $text alertnote]]} {
set text "<[htmlSetCase AREA]"
}
} else {
set text ""
}
}
if {[string length $text] } {append text ">"}
return ${text}
}
proc htmlWrapTag {toadd} {
global htmlWrapPos fillColumn HTMLmodeVars
if {!$HTMLmodeVars(wordWrap)} {return " $toadd"}
incr htmlWrapPos [string length $toadd]
if {$htmlWrapPos > $fillColumn} {
set htmlWrapPos [string length $toadd]
return "\r$toadd"
} else {
return " $toadd"
}
}
# these two require at least one of several optional attributes
proc htmlFontBaseTest {text cmd} {
if {([string toupper $text] == "<FONT" || [string toupper $text] == "<BASE" )} {
eval {$cmd "At least one of the attributes is required."}
return 1
}
return 0
}
# HREF or NAME must be used for A.
proc htmlATest {text cmd} {
if {![regexp -nocase {href=} $text] && ![regexp -nocase {name=} $text]} {
eval {$cmd "At least one of the attributes HREF and NAME must be used."}
return 1
}
return 0
}
# ROWS or COLS must be used for FRAMESET
proc htmlFramesetTest {text cmd} {
if {![regexp -nocase {rows=} $text] && ![regexp -nocase {cols=} $text]} {
eval {$cmd "At least one of the attributes ROWS and COLS must be used."}
return 1
}
return 0
}
# Some checks for SPACER.
proc htmlSpacerTest {text cmd} {
set horver [regexp -nocase {type=\"(horizontal|vertical)\"} $text]
set wh [regexp -nocase {width=|height=} $text]
set sz [regexp -nocase {size=} $text]
set al [regexp -nocase {align=} $text]
set invalidInput 1
if {$horver && ($wh || $al)} {
eval {$cmd "WIDTH, HEIGHT and ALIGN should only be used when TYPE=BLOCK."}
} elseif {!$horver && $sz} {
eval {$cmd "SIZE should only be used when TYPE=HORIZONTAL or VERTICAL."}
} elseif {$horver && !$sz} {
eval {$cmd "SIZE is required when TYPE=HORIZONTAL or VERTICAL."}
} elseif {!$horver && !$wh} {
eval {$cmd "WIDTH or HEIGHT is required when TYPE=BLOCK."}
} else {
set invalidInput 0
}
return $invalidInput
}
# For AREA, either HREF or NOHREF must be used, but not both.
proc htmlAreaTest {text cmd} {
set hasHref [regexp -nocase {href=} $text]
set hasNohref [regexp -nocase {nohref} $text]
set hasCoords [regexp -nocase {coords=} $text]
set shapeDefault [regexp -nocase {shape=\"default\"} $text]
set invalidInput 0
if {($hasHref && $hasNohref) || (!$hasHref && !$hasNohref)} {
eval {$cmd "One of the attributes HREF and NOHREF must be used, but not both."}
set invalidInput 1
} elseif {!$hasCoords && !$shapeDefault} {
eval {$cmd "COORDS= is required if SHAPE≠DEFAULT"}
set invalidInput 1
}
return $invalidInput
}
# Adds a NAME= value to cache.
proc htmlOpenExtraThings {elem attr val} {
if {[lsearch -exact {A MAP} $elem] >= 0 && $attr == "NAME="} {
htmlAddToCache URLs "#$val"
}
if {$elem == "FRAME" && $attr == "NAME="} {
htmlAddToCache windows $val
}
}
# Check if a color number is a valid number, or one of the predefined names.
# Returns 0 if not and the color number if it is.
proc htmlCheckColorNumber {color} {
global htmlColorName
set color [string tolower $color]
if {[info exists htmlColorName($color)]} {return $htmlColorName($color)}
if {[string index $color 0] != "#"} {
set color "#${color}"
}
set color [string toupper $color]
if {[string length $color] != 7 || ![regexp {^#[0-9A-F]+$} $color]} {
return 0
} else {
return $color
}
}
# Adds a URL or window given as input to cache
proc htmlAddToCache {cache newurl} {
global modifiedModeVars HTMLmodeVars
if {$cache == "windows" && [lsearch -exact {_self _top _parent _blank} $newurl] >= 0} {return}
set URLs $HTMLmodeVars($cache)
if {[string length $newurl] && [lsearch -exact $URLs $newurl] < 0} {
set URLs [lsort [lappend URLs $newurl]]
set HTMLmodeVars($cache) $URLs
lappend modifiedModeVars [list $cache HTMLmodeVars]
if {[llength $URLs] == 1} {htmlEnable$cache on}
}
}
# Check if a input is a valid number for the element attribute.
# Returns 1 if it is, otherwise returns an error message.
proc htmlCheckAttrNumber {item attr number} {
set attrNumbers [htmlGetNumber $item]
set numind [lsearch $attrNumbers "${attr}*"]
set numstr [string range [lindex $attrNumbers $numind] [string length $attr] end]
regexp {^[-0-9]+} $numstr minvalue
set numstr [string range $numstr [expr [string length $minvalue] + 1] end]
regexp {^[-i0-9]+} $numstr maxvalue
set procent [string range $numstr [expr [string length $numstr] - 1] end]
if {$procent == "%"} {
set procerr " or percentage"
} else {
set procerr ""
}
if {$maxvalue == "i"} {
set errtext "A number $minvalue or greater"
} else {
set errtext "A number in the range $minvalue to $maxvalue"
}
if {$item == "FONT"} { append errtext " or -6 to +6"}
append errtext "$procerr expected."
# Is percent allowed?
if {[string index $number [expr [string length $number] - 1]] == "%" } {
set number [string range $number 0 [expr [string length $number] - 2]]
if {$procent != "%"} {return $errtext}
}
# FONT can take values -6 - +6. Special case.
if {$item == "FONT" && [regexp {^(\+|-)[1-6]$} $number]} { return 1}
# Is input a number?
if {![regexp {^-?[0-9]+$} $number]} {return $errtext}
# Is input in the valid range?
if {( $maxvalue != "i" && $number > $maxvalue ) || $number < $minvalue } {
return $errtext
}
return 1
}
# Add quotes to attribute
proc htmlAddQuotes {v} {
if {[string range $v 0 0] != "\""} {set v "\"$v"}
set vlen [expr [string length $v] - 1]
if {[string range $v $vlen $vlen] !="\""} {append v "\""}
return $v
}
# Splits an attribute into its name and value and remove quotes.
proc htmlRemoveQuotes {attrStr} {
# Is it a flag?
if {![string match "*=*" $attrStr]} {return [string toupper $attrStr]}
set attr [string range $attrStr 0 [string first "=" $attrStr]]
# Get the attribute value.
set attrVal [string range $attrStr [expr [string first "=" $attrStr] + 1] end]
return [list $attr [string trim $attrVal \"]]
}
# Closing tag of an element
proc htmlCloseElem {theElem} {
return "</[htmlSetCase $theElem]>"
}
#
# Element build routines
#
# Build elements with only a opening tag.
proc htmlBuildOpening {ftype {begCR 0} {endCR 0} {attr ""}} {
set text1 ""
if {$begCR} { set text1 [htmlOpenCR]}
set text [htmlOpenElem $ftype $attr]
if {![string length $text]} {return}
if {$endCR} {append text "\r"}
insertText $text1 $text
}
# This is used for almost all containers
proc htmlBuildElem {ftype {attr ""}} {
global HTMLmodeVars htmlCurSel htmlIsSel
if {![string length [set text [htmlOpenElem $ftype $attr]]]} {return}
htmlGetSel
append text $htmlCurSel
set currpos [expr [getPos] + [string length $text]]
append text [htmlCloseElem $ftype]
if {!$htmlIsSel && $HTMLmodeVars(useTabMarks)} {append text "•"}
if {$htmlIsSel} {
replaceText [getPos] [selEnd] $text
} else {
insertText $text
goto $currpos
}
}
# This is used for elements that should be surrounded by newlines
proc htmlBuildCRElem {ftype {extrablankline 0} {attr ""}} {
global htmlCurSel htmlIsSel HTMLmodeVars
set text [htmlOpenCR $extrablankline]
if {![string length [set text2 [htmlOpenElem $ftype $attr 0]]]} {return}
append text $text2
htmlGetSel
append text $htmlCurSel
set currpos [expr [getPos] + [string length $text]]
append text [htmlCloseElem $ftype]
append text "\r"
if {$extrablankline} {append text "\r"}
if {!$htmlIsSel && $HTMLmodeVars(useTabMarks)} {append text "•"}
if {$htmlIsSel} { deleteSelection }
insertText $text
if {!$htmlIsSel} {
goto $currpos
}
}
# This is used for elements that should be surrounded by empty lines
proc htmlBuildCR2Elem {ftype {attr ""}} {
global HTMLmodeVars htmlCurSel htmlIsSel
set text [htmlOpenCR 1]
# Check if user has skipped an attribute which can't be skipped.
if {![string length [set text2 [htmlOpenElem $ftype $attr 0]]]} {return}
append text $text2
htmlGetSel
if {$htmlIsSel || $ftype != "SCRIPT"} {
append text "\r$htmlCurSel"
} else {
append text "\r<!-- Hide content from old browsers\r"
}
set currpos [expr [getPos] + [string length $text]]
append text "\r"
if {!$htmlIsSel && $ftype == "SCRIPT"} {append text "// end hiding content from old browsers -->\r"}
append text [htmlCloseElem $ftype]
append text "\r\r"
if {!$htmlIsSel && $HTMLmodeVars(useTabMarks)} {append text "•"}
if {$htmlIsSel} { deleteSelection }
insertText $text
if {!$htmlIsSel} {
goto $currpos
}
}
# Determines which list the current position is inside.
proc htmlFindList {} {
set listType ""
foreach l [list UL OL DIR MENU] {
set ex "<${l}(\[ \\t\\r\]+\[^>\]*>|>)"
set listOpening [search -s -f 0 -i 1 -r 1 -m 0 -n $ex [getPos]]
set ex2 </$l>
set listClosing [search -s -f 0 -i 1 -r 1 -m 0 -n $ex2 [getPos]]
# Search until a single list opening is found.
while {[string length $listOpening] && [string length $listClosing] &&
[lindex $listClosing 0] > [lindex $listOpening 0]} {
set listOpening [search -s -f 0 -i 1 -r 1 -m 0 -n $ex [expr [lindex $listOpening 0] - 1]]
set listClosing [search -s -f 0 -i 1 -r 1 -m 0 -n $ex2 [expr [lindex $listClosing 0] - 1]]
}
if {[string length $listOpening]} {
lappend listType "$listOpening $l"
}
}
set ltype [lindex [lindex $listType 0] 2]
set lnum [lindex [lindex $listType 0] 0]
for {set i 1} {$i < [llength $listType]} {incr i} {
if {[lindex [lindex $listType $i] 0] > $lnum} {
set ltype [lindex [lindex $listType $i] 2]
set lnum [lindex [lindex $listType $i] 0]
}
}
return $ltype
}